﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Security;
using System.Text;
using System.Threading.Tasks;

namespace WJP.Logging
{
    /// <summary>
    /// 使用<see cref="TraceSource"/>记录日志
    /// </summary>
    [Serializable]
    public class TraceLogger : AbstractLogger
    {
        private static readonly Dictionary<string, TraceSource> cache = new Dictionary<string, TraceSource>();

        private TraceSource traceSource;

        [SecuritySafeCritical]
        public TraceLogger(string name) : base(name)
        {
            Initialize();
            Level = MapLoggerLevel(traceSource.Switch.Level);
        }

        [SecuritySafeCritical]
        public TraceLogger(string name, LogLevel level) : base(name, level)
        {
            Initialize();
            Level = MapLoggerLevel(traceSource.Switch.Level);
        }

        [SecuritySafeCritical]
        public override ILogger CreateChildLogger(string loggerName)
        {
            return InternalCreateChildLogger(loggerName);
        }

        [SecurityCritical]
        private ILogger InternalCreateChildLogger(string loggerName)
        {
            return new TraceLogger(Name + "." + loggerName, base.Level);
        }

        protected override void Log(LogLevel loggerLevel, string loggerName, string message, Exception exception)
        {
            if (exception == null)
            {
                traceSource.TraceEvent(MapTraceEventType(loggerLevel), 0, message);
                return;
            }
            traceSource.TraceData(MapTraceEventType(loggerLevel), 0, new object[]
            {
                message,
                exception
            });
        }

        [SecurityCritical]
        private void Initialize()
        {
            lock (cache)
            {
                if (!cache.TryGetValue(base.Name, out this.traceSource))
                {
                    SourceLevels defaultLevel = MapSourceLevels(base.Level);
                    this.traceSource = new TraceSource(base.Name, defaultLevel);
                    if (IsSourceConfigured(this.traceSource))
                    {
                        cache.Add(base.Name, this.traceSource);
                    }
                    else
                    {
                        TraceSource traceSource = new TraceSource("Default", defaultLevel);
                        string text = ShortenName(base.Name);
                        while (!string.IsNullOrEmpty(text))
                        {
                            TraceSource traceSource2 = new TraceSource(text, defaultLevel);
                            if (IsSourceConfigured(traceSource2))
                            {
                                traceSource = traceSource2;
                                break;
                            }
                            text = ShortenName(text);
                        }
                        this.traceSource.Switch = traceSource.Switch;
                        this.traceSource.Listeners.Clear();
                        foreach (object obj2 in traceSource.Listeners)
                        {
                            TraceListener listener = (TraceListener)obj2;
                            this.traceSource.Listeners.Add(listener);
                        }
                        cache.Add(base.Name, this.traceSource);
                    }
                }
            }
        }

        private static string ShortenName(string name)
        {
            int num = name.LastIndexOf('.');
            if (num != -1)
            {
                return name.Substring(0, num);
            }
            return null;
        }

        [SecuritySafeCritical]
        private static bool IsSourceConfigured(TraceSource source)
        {
            return source.Listeners.Count != 1 || !(source.Listeners[0] is DefaultTraceListener) || !(source.Listeners[0].Name == "Default");
        }

        private static LogLevel MapLoggerLevel(SourceLevels level)
        {
            if (level <= SourceLevels.Warning)
            {
                switch (level)
                {
                    case SourceLevels.All:
                        return LogLevel.Debug;
                    case SourceLevels.Off:
                    case (SourceLevels)2:
                        break;
                    case SourceLevels.Critical:
                        return LogLevel.Fatal;
                    case SourceLevels.Error:
                        return LogLevel.Error;
                    default:
                        if (level == SourceLevels.Warning)
                        {
                            return LogLevel.Warn;
                        }
                        break;
                }
            }
            else
            {
                if (level == SourceLevels.Information)
                {
                    return LogLevel.Info;
                }
                if (level == SourceLevels.Verbose)
                {
                    return LogLevel.Debug;
                }
            }
            return LogLevel.Off;
        }

        private static SourceLevels MapSourceLevels(LogLevel level)
        {
            switch (level)
            {
                case LogLevel.Fatal:
                    return SourceLevels.Critical;
                case LogLevel.Error:
                    return SourceLevels.Error;
                case LogLevel.Warn:
                    return SourceLevels.Warning;
                case LogLevel.Info:
                    return SourceLevels.Information;
                case LogLevel.Debug:
                    return SourceLevels.Verbose;
                default:
                    return SourceLevels.Off;
            }
        }

        private static TraceEventType MapTraceEventType(LogLevel level)
        {
            switch (level)
            {
                case LogLevel.Fatal:
                    return TraceEventType.Critical;
                case LogLevel.Error:
                    return TraceEventType.Error;
                case LogLevel.Warn:
                    return TraceEventType.Warning;
                case LogLevel.Info:
                    return TraceEventType.Information;
                case LogLevel.Debug:
                    return TraceEventType.Verbose;
                default:
                    return TraceEventType.Verbose;
            }
        }
    }
}
